//
//  XcodeCleanManager.swift
//  ZJTools
//
//  Created by 万孟超 on 2023/11/17.
//

import Foundation

extension NSNotification.Name {
    static let XcodeCleanReceive = Self.init("XcodeCleanReceiveNotification")
    static let XcodeCleanHomeStatistics = Self.init("XcodeCleanReceiveNotification")
}


class XcodeCleanManager {
    
    let defaultPath = "/Users/\(NSUserName())/Library/Developer"
    
    static let shared = XcodeCleanManager()
    
    private init() { }
    
    var deriveDataModel = CleanModel(path: "/Users/\(NSUserName())/Library/Developer" + "/Xcode/DerivedData") 
    var cacheModel = CleanModel(path: "/Users/\(NSUserName())/Library/Developer" + "/CoreSimulator/Caches")
    var archiveModel = CleanModel(path: "/Users/\(NSUserName())/Library/Developer" + "/Xcode/Archives")
    var deviceSupportModel = CleanModel(path: "/Users/\(NSUserName())/Library/Developer" + "/Xcode/iOS DeviceSupport")
    var simulatorModel = CleanModel(path: "/Users/\(NSUserName())/Library/Developer" + "/CoreSimulator/Devices")
    
    
    func analyse(finish finishBlock: @escaping () -> ()) {
        File.requestFolderPermissions(defaultPath, successBlock: {[self] analyseFinish in
            cleanData()
            let group = DispatchGroup()
            group.enter()
            Async(.custom(name: "derivedDataPath")) {[self] in
                File.searchDerivedData(deriveDataModel.path) {[self] currentSize in
                    deriveDataModel.size = currentSize
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 1, "size": currentSize, "isFreshFloder" : false])
                        .post(.XcodeCleanHomeStatistics, userInfo: ["index": 1])
                } floderBlock: {[self] in
                    deriveDataModel.subPath.append(CleanModel.FloderModel(path: $0, size: $1))
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 1, "isFreshFloder" : true])
                } finishBlock: {
                    group.leave()
                }
            }
            group.enter()
            Async(.custom(name: "deviceSupportPath")) {[self] in
                File.searchDerivedData(deviceSupportModel.path) {[self] currentSize in
                    deviceSupportModel.size = currentSize
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 4, "size": currentSize, "isFreshFloder" : false])
                        .post(.XcodeCleanHomeStatistics, userInfo: ["index": 4])
                } floderBlock: {[self] in
                    deviceSupportModel.subPath.append(CleanModel.FloderModel(path: $0, size: $1))
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 4, "isFreshFloder" : true])
                } finishBlock: {
                    group.leave()
                }
            }
            group.enter()
            Async(.custom(name: "simulatorPath")) {[self] in
                File.searchDerivedData(simulatorModel.path) {[self] currentSize in
                    simulatorModel.size = currentSize
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 5, "size": currentSize, "isFreshFloder" : false])
                        .post(.XcodeCleanHomeStatistics, userInfo: ["index": 5])
                    
                } floderBlock: {[self] in
                    if $0 != "device_set.plist" {
                        simulatorModel.subPath.append(CleanModel.FloderModel(path: $0, size: $1))
                        NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 5, "isFreshFloder" : true])
                    }
                } finishBlock: {
                    group.leave()
                }
            }
            group.enter()
            Async(.custom(name: "cachePath")) {[self] in
                File.searchDerivedData(cacheModel.path) {[self] currentSize in
                    cacheModel.size = currentSize
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 2, "size": currentSize, "isFreshFloder" : false])
                        .post(.XcodeCleanHomeStatistics, userInfo: ["index": 2])
                } floderBlock: {[self] in
                    cacheModel.subPath.append(CleanModel.FloderModel(path: $0, size: $1))
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 2, "isFreshFloder" : true])
                } finishBlock: {
                    group.leave()
                }
            }
            group.enter()
            Async(.custom(name: "archivePath")) {[self] in
                File.searchDerivedData(archiveModel.path) {[self] currentSize in
                    archiveModel.size = currentSize
                    let userInfo = ["index": 3, "size": currentSize, "isFreshFloder" : false]
                    NotiManager.post(.XcodeCleanReceive, userInfo: userInfo).post(.XcodeCleanHomeStatistics, userInfo: ["index": 3])
                } floderBlock: {[self] in
                    archiveModel.subPath.append(CleanModel.FloderModel(path: $0, size: $1))
                    NotiManager.post(.XcodeCleanReceive, userInfo: ["index": 3, "isFreshFloder" : true])
                } finishBlock: {
                    group.leave()
                }
            }
            Async(.custom(name: "finish")) {
                group.wait()
                finishBlock()
                analyseFinish()
            }
        }, cancelBlock: finishBlock)
    }
    
    func cleanData() {
        deriveDataModel.size = 0
        deriveDataModel.subPath = []
        deviceSupportModel.size = 0
        deviceSupportModel.subPath = []
        simulatorModel.size = 0
        simulatorModel.subPath = []
        cacheModel.size = 0
        cacheModel.subPath = []
        archiveModel.size = 0
        archiveModel.subPath = []
    }
    
    func onekeyCleanXcode(finish: @escaping () -> ()) {
        analyse {[weak self] in
            self?.cleanData(finish: finish)
        }
    }
    
    func cleanData(finish: @escaping () -> ()) {
        let model = XcodeCleanManager.shared.deriveDataModel
        let group = DispatchGroup()
        for subPath in model.subPath {
            let path = model.path + "/" + subPath.path
            group.enter()
            File.removePath(path) { _ in
                group.leave()
            }
        }
        group.wait()
        finish()
    }
}

extension XcodeCleanManager {
    struct CleanModel {
        
        struct FloderModel {
            var path: String
            var size: Double
        }
        
        var path: String
        var size: Double = 0
        var subPath: [FloderModel] = []
    }
    
    enum CleanModelType {
        case deriveData
        case deviceSupport
        case simulator
        case cache
        case archive
        
        func obtainCleanModel() -> CleanModel {
            switch self {
                case .deriveData:
                    return XcodeCleanManager.shared.deriveDataModel
                case .deviceSupport:
                    return XcodeCleanManager.shared.deviceSupportModel
                case .simulator:
                    return XcodeCleanManager.shared.simulatorModel
                case .cache:
                    return XcodeCleanManager.shared.cacheModel
                case .archive:
                    return XcodeCleanManager.shared.archiveModel
            }
        }
        
        func freshManagerData(_ model: CleanModel) {
            var index: Int = 1
            switch self {
                case .deriveData:
                    XcodeCleanManager.shared.deriveDataModel = model
                    index = 1
                case .deviceSupport:
                    XcodeCleanManager.shared.deviceSupportModel = model
                    index = 4
                case .simulator:
                    XcodeCleanManager.shared.simulatorModel = model
                    index = 5
                case .cache:
                    XcodeCleanManager.shared.cacheModel = model
                    index = 2
                case .archive:
                    XcodeCleanManager.shared.archiveModel = model
                    index = 3
            }
            NotiManager.post(.XcodeCleanReceive, userInfo: ["index": index, "size": model.size, "isFreshFloder" : false])
                .post(.XcodeCleanReceive, userInfo: ["index": index, "isFreshFloder" : true])
        }
        
        func titleDes() -> String {
            switch self {
                case .deriveData:
                    return "项目的一些索引以及缓存文件，可以安全删除，删除后再次打开项目时会自动生成"
                case .deviceSupport:
                    return "iOS真机调试文件，设备连接时会生成对应系统版本的调试信息，只需要保留持有设备最新的版本"
                case .simulator:
                    return "模拟器中安装应用保存的数据，建议使用xcrun simctl delete unavailable清理"
                case .cache:
                    return "模拟器运行时生成的缓存文件"
                case .archive:
                    return "打包的归档文件"
            }
        }
        
        func currentTitle() -> String {
            switch self {
                case .deriveData:
                    return Self.currentTitle(0)
                case .deviceSupport:
                    return Self.currentTitle(1)
                case .simulator:
                    return Self.currentTitle(2)
                case .cache:
                    return Self.currentTitle(3)
                case .archive:
                    return Self.currentTitle(4)
            }
        }
        
        static func currentTitle(_ index: Int) -> String {
            if index == 0 {
                return "Derived Data"
            }else if index == 1 {
                return "Cache"
            }else if index == 2 {
                return "Archive"
            }else if index == 3 {
                return "Device Support"
            }else {
                return "Simulator"
            }
        }
        
        static var count: Int {
            return 5
        }
        
        static func indexModel(_ index: Int) -> CleanModel {
            if index == 0 {
                return XcodeCleanManager.shared.deriveDataModel
            }else if index == 1 {
                return XcodeCleanManager.shared.cacheModel
            }else if index == 2 {
                return XcodeCleanManager.shared.archiveModel
            }else if index == 3 {
                return XcodeCleanManager.shared.deviceSupportModel
            }else {
                return XcodeCleanManager.shared.simulatorModel
            }
        }
    }
}
